﻿//------------------------------------------------------------------------------
// Copyright © FRA & FV 2014
// All rights reserved
//------------------------------------------------------------------------------
// Binary Serialization SDK
//
// SVN revision information:
//   $Revision: 93 $
//------------------------------------------------------------------------------
using System;
using System.IO;

namespace FRAFV.Binary.Serialization
{
	/// <summary>Converts data types to an array of bytes, and an array of bytes to data types.</summary>
	public static class BinConverter
	{
		private const long MaxSeconds = 315537897599L;
		private const long TicksPerSecond = 10000000L;
		private const long TicksPerDay = 864000000000L;
		private const long SecondsPerDay = 86400;
		private const long DaysTo1970 = 719162;
		private const long DaysTo1904 = 695055;

		#region Synch safe int 64-bit
		/// <summary>Returns a 63-bit synch safe unsigned integer converted from up to nine bytes at a specified position
		///   in a byte array.</summary>
		/// <returns>A 63-bit synch safe unsigned integer formed by <paramref name="size" /> bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="size">The array of bytes length.</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than the length of <paramref name="value" /> minus <paramref name="size" />,
		///   and is less than or equal to the length of <paramref name="value" /> minus 1.
		/// –or–
		///   <paramref name="size" /> is greater than 9.
		/// –or–
		///   A highest bit (bit 7) isn't zero in a byte array.
		/// </exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static long ToSynchSafeInt64(this byte[] value, int startIndex, int size)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - size)
				throw new ArgumentException("startIndex");
			if (size > 9)
				throw new ArgumentException("size");
			for (int k = startIndex; k < startIndex + size; k++)
				if ((value[k] & 0x80) != 0)
					throw new ArgumentException("Invalid synch safe data");
			long res = 0L;
			for (int k = startIndex; k < startIndex + size; k++)
				res |= (res << 7) | value[k];
			return res;
		}

		/// <summary>Reads a synch safe unsigned integer from the current stream and advances the current position of
		///   the stream by up to nine bytes.</summary>
		/// <param name="reader">Binary reader.</param>
		/// <param name="size">The array of bytes length.</param>
		/// <returns>A synch safe unsigned integer read from this stream.</returns>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="size" /> is greater than 9.
		/// –or–
		///   A highest bit (bit 7) isn't zero in a byte array.
		/// </exception>
		/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <filterpriority>2</filterpriority>
		public static long ReadSynchSafeInt64(this BinaryReader reader, int size)
		{
			return reader.ReadBytes(size).ToSynchSafeInt64(0, size);
		}

		/// <summary>Returns the specified 63-bit synch safe unsigned integer value as an array of bytes.</summary>
		/// <param name="size">The array of bytes length.</param>
		/// <returns>An array of bytes with length up to 9.</returns>
		/// <param name="value">The synch safe number to convert. </param>
		/// <exception cref="T:System.OverflowException"><paramref name="value" /> is less than zero.</exception>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="size" /> is greater than 9.</exception>
		public static byte[] GetSynchSafeBytes(this long value, int size)
		{
			if (value < 0L)
				throw new OverflowException("Synch safe integer is out of range");
			if (size > 9)
				throw new ArgumentException("size");
			var buffer = new byte[size];
			for(int pos = size - 1; value > 0x80L && pos >= 0; pos--)
			{
				buffer[pos] = (byte)(value & 0x7FL);
				value >>= 7;
			}
			return buffer;
		}

		/// <summary>Writes a synch safe unsigned integer to the current stream and advances the stream position
		///   by up to nine bytes.</summary>
		/// <param name="writer">Binary Writer.</param>
		/// <param name="value">The synch safe unsigned integer to write. </param>
		/// <param name="size">The synch safe storage length.</param>
		/// <exception cref="T:System.OverflowException"><paramref name="value" /> is less than zero.</exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public static void WriteSynchSafe(this BinaryWriter writer, long value, int size)
		{
			writer.Write(value.GetSynchSafeBytes(size));
		}
		#endregion

		#region Synch safe int 32-bit
		/// <summary>Returns a 28-bit synch safe unsigned integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A 28-bit synch safe unsigned integer formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3,
		///   and is less than or equal to the length of <paramref name="value" /> minus 1.
		/// –or–
		/// A highest bit (bit 7) isn't zero in a byte array.
		/// </exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static int ToSynchSafeInt32(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			if ((value[startIndex] & 0x80) != 0 ||
				(value[startIndex + 1] & 0x80) != 0 ||
				(value[startIndex + 2] & 0x80) != 0 ||
				(value[startIndex + 3] & 0x80) != 0)
				throw new ArgumentException("Invalid synch safe data");
			return
				value[startIndex] << 21 |
				value[startIndex + 1] << 14 |
				value[startIndex + 2] << 7 |
				value[startIndex + 3];
		}

		/// <summary>Reads a 4-byte synch safe unsigned integer from the current stream and advances the current position of
		///   the stream by four bytes.</summary>
		/// <param name="reader">Binary reader.</param>
		/// <returns>A 4-byte synch safe unsigned integer read from this stream.</returns>
		/// <exception cref="T:System.ArgumentException">A highest bit (bit 7) isn't zero in the current stream.</exception>
		/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <filterpriority>2</filterpriority>
		public static int ReadSynchSafeInt32(this BinaryReader reader)
		{
			return reader.ReadBytes(4).ToSynchSafeInt32(0);
		}

		/// <summary>Returns the specified 28-bit synch safe unsigned integer value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The synch safe number to convert. </param>
		/// <exception cref="T:System.OverflowException"><paramref name="value" /> is greater than maximum
		///   <c>0xFFFFFFF</c> synch safe value or less than zero.</exception>
		public static byte[] GetSynchSafeBytes(this int value)
		{
			if (value >= 0x10000000 || value < 0)
				throw new OverflowException("Synch safe integer is out of range");
			var buffer = new byte[4];
			buffer[0] = (byte)((value >> 21) & 0x7F);
			buffer[1] = (byte)((value >> 14) & 0x7F);
			buffer[2] = (byte)((value >> 7) & 0x7F);
			buffer[3] = (byte)(value & 0x7F);
			return buffer;
		}

		/// <summary>Writes a 4-byte synch safe unsigned integer to the current stream and advances the stream position by four bytes.</summary>
		/// <param name="writer">Binary Writer.</param>
		/// <param name="value">The 4-byte synch safe unsigned integer to write. </param>
		/// <exception cref="T:System.OverflowException"><paramref name="value" /> is greater than maximum
		///   <c>0xFFFFFFF</c> synch safe value or less than zero.</exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public static void WriteSynchSafe(this BinaryWriter writer, int value)
		{
			writer.Write(value.GetSynchSafeBytes());
		}
		#endregion

		#region Synch safe word 16-bit
		/// <summary>Returns a 14-bit synch safe unsigned integer converted from two bytes at a specified position in a byte array.</summary>
		/// <returns>A 14-bit synch safe unsigned integer formed by two bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> equals the length of <paramref name="value" /> minus 1.
		/// –or–
		/// A highest bit (bit 7) isn't zero in a byte array.
		/// </exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static ushort ToSynchSafeInt16(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 2)
				throw new ArgumentException("startIndex");
			if ((value[startIndex] & 0x80) != 0 || (value[startIndex + 1] & 0x80) != 0)
				throw new ArgumentException("Invalid synch safe data");
			return (ushort)(value[startIndex] << 7 | value[startIndex + 1]);
		}

		/// <summary>Reads a 2-byte synch safe unsigned integer from the current stream and advances the current position of
		///   the stream by two bytes.</summary>
		/// <param name="reader">Binary reader.</param>
		/// <returns>A 2-byte synch safe unsigned integer read from this stream.</returns>
		/// <exception cref="T:System.ArgumentException">A highest bit (bit 7) isn't zero in the current stream.</exception>
		/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <filterpriority>2</filterpriority>
		public static ushort ReadSynchSafeInt16(this BinaryReader reader)
		{
			return reader.ReadBytes(2).ToSynchSafeInt16(0);
		}

		/// <summary>Returns the specified 14-bit synch safe unsigned integer value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 2.</returns>
		/// <param name="value">The synch safe number to convert. </param>
		/// <exception cref="T:System.OverflowException"><paramref name="value" /> is greater than maximum
		///   <c>0x3FFF</c> synch safe value.</exception>
		public static byte[] GetSynchSafeBytes(this ushort value)
		{
			if (value >= 0x4000)
				throw new OverflowException("Synch safe integer is out of range");
			var buffer = new byte[2];
			buffer[0] = (byte)((value >> 7) & 0x7F);
			buffer[1] = (byte)(value & 0x7F);
			return buffer;
		}

		/// <summary>Writes a 2-byte synch safe unsigned integer to the current stream and advances the stream position by two bytes.</summary>
		/// <param name="writer">Binary Writer.</param>
		/// <param name="value">The 2-byte synch safe unsigned integer to write. </param>
		/// <exception cref="T:System.OverflowException"><paramref name="value" /> is greater than maximum
		///   <c>0x3FFF</c> synch safe value.</exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public static void WriteSynchSafe(this BinaryWriter writer, ushort value)
		{
			writer.Write(value.GetSynchSafeBytes());
		}
		#endregion

		#region int 64-bit big endian
		internal static byte[] InternalFromInt64(long value, bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			if (littleEndian)
			{
				buffer[startIndex] = (byte)value;
				buffer[startIndex + 1] = (byte)(value >> 8);
				buffer[startIndex + 2] = (byte)(value >> 16);
				buffer[startIndex + 3] = (byte)(value >> 24);
				buffer[startIndex + 4] = (byte)(value >> 32);
				buffer[startIndex + 5] = (byte)(value >> 40);
				buffer[startIndex + 6] = (byte)(value >> 48);
				buffer[startIndex + 7] = (byte)(value >> 56);
			}
			else
			{
				buffer[startIndex + 7] = (byte)value;
				buffer[startIndex + 6] = (byte)(value >> 8);
				buffer[startIndex + 5] = (byte)(value >> 16);
				buffer[startIndex + 4] = (byte)(value >> 24);
				buffer[startIndex + 3] = (byte)(value >> 32);
				buffer[startIndex + 2] = (byte)(value >> 40);
				buffer[startIndex + 1] = (byte)(value >> 48);
				buffer[startIndex] = (byte)(value >> 56);
			}
			return buffer;
		}

		internal static ulong InternalToUInt64(bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			if (littleEndian)
			{
				uint d1 = (uint)(
					buffer[startIndex] |
					buffer[startIndex + 1] << 8 |
					buffer[startIndex + 2] << 16 |
					buffer[startIndex + 3] << 24);
				uint d2 = (uint)(
					buffer[startIndex + 4] |
					buffer[startIndex + 5] << 8 |
					buffer[startIndex + 6] << 16 |
					buffer[startIndex + 7] << 24);
				return (ulong)d2 << 32 | (ulong)d1;
			}
			else
			{
				uint d2 = (uint)(
					buffer[startIndex + 7] |
					buffer[startIndex + 6] << 8 |
					buffer[startIndex + 5] << 16 |
					buffer[startIndex + 4] << 24);
				uint d1 = (uint)(
					buffer[startIndex + 3] |
					buffer[startIndex + 2] << 8 |
					buffer[startIndex + 1] << 16 |
					buffer[startIndex] << 24);
				return (ulong)d1 << 32 | (ulong)d2;
			}
		}

		/// <summary>Returns the specified 64-bit signed integer value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 8.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetBytes(this long value)
		{
			return InternalFromInt64(value, BitConverter.IsLittleEndian, new byte[8]);
		}

		/// <summary>Returns the specified 64-bit signed integer value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 8.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetBytes(this long value, bool littleEndian)
		{
			return InternalFromInt64(value, littleEndian, new byte[8]);
		}

		/// <summary>Returns the specified 64-bit unsigned integer value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 8.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetBytes(this ulong value)
		{
			return InternalFromInt64((long)value, BitConverter.IsLittleEndian, new byte[8]);
		}

		/// <summary>Returns the specified 64-bit unsigned integer value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 8.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetBytes(this ulong value, bool littleEndian)
		{
			return InternalFromInt64((long)value, littleEndian, new byte[8]);
		}

		/// <summary>Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.</summary>
		/// <returns>A 64-bit signed integer formed by eight bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 7, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static long ToInt64(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 8)
				throw new ArgumentException("startIndex");
			return (long)InternalToUInt64(BitConverter.IsLittleEndian, value, startIndex);
		}

		/// <summary>Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.</summary>
		/// <returns>A 64-bit signed integer formed by eight bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 7, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static long ToInt64(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 8)
				throw new ArgumentException("startIndex");
			return (long)InternalToUInt64(littleEndian, value, startIndex);
		}

		/// <summary>Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array.</summary>
		/// <returns>A 64-bit unsigned integer formed by eight bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 7, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static ulong ToUInt64(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 8)
				throw new ArgumentException("startIndex");
			return InternalToUInt64(BitConverter.IsLittleEndian, value, startIndex);
		}

		/// <summary>Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array.</summary>
		/// <returns>A 64-bit unsigned integer formed by eight bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 7, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static ulong ToUInt64(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 8)
				throw new ArgumentException("startIndex");
			return InternalToUInt64(littleEndian, value, startIndex);
		}
		#endregion

		#region int 24-bit big endian
		internal static byte[] InternalFromInt24(int value, bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			if (littleEndian)
			{
				buffer[startIndex] = (byte)value;
				buffer[startIndex + 1] = (byte)(value >> 8);
				buffer[startIndex + 2] = (byte)(value >> 16);
			}
			else
			{
				buffer[startIndex + 2] = (byte)value;
				buffer[startIndex + 1] = (byte)(value >> 8);
				buffer[startIndex] = (byte)(value >> 16);
			}
			return buffer;
		}

		internal static int InternalToInt24(bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			if (littleEndian)
			{
				return
					buffer[startIndex] |
					buffer[startIndex + 1] << 8 |
					(sbyte)buffer[startIndex + 2] << 16;
			}
			else
			{
				return
					buffer[startIndex + 2] |
					buffer[startIndex + 1] << 8 |
					(sbyte)buffer[startIndex] << 16;
			}
		}

		internal static int InternalToUInt24(bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			if (littleEndian)
			{
				return
					buffer[startIndex] |
					buffer[startIndex + 1] << 8 |
					buffer[startIndex + 2] << 16;
			}
			else
			{
				return
					buffer[startIndex + 2] |
					buffer[startIndex + 1] << 8 |
					buffer[startIndex] << 16;
			}
		}

		/// <summary>Returns the specified 24-bit integer value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 3.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetInt24Bytes(this int value)
		{
			return InternalFromInt24(value, BitConverter.IsLittleEndian, new byte[3]);
		}

		/// <summary>Returns the specified 24-bit integer value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 3.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetInt24Bytes(this int value, bool littleEndian)
		{
			return InternalFromInt24(value, littleEndian, new byte[3]);
		}

		/// <summary>Writes a 3-byte integer to the current stream and advances the stream position by three bytes.</summary>
		/// <param name="writer">Binary writer.</param>
		/// <param name="value">The 3-byte integer to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public static void WriteInt24(this BinaryWriter writer, int value)
		{
			var bin = writer as BinWriter;
			if (bin != null)
				bin.WriteInt24(value);
			else
				writer.Write(InternalFromInt24(value, BitConverter.IsLittleEndian, new byte[3]));
		}

		/// <summary>Returns a 24-bit signed integer converted from three bytes at a specified position in a byte array.</summary>
		/// <returns>A 24-bit signed integer formed by three bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 2, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static int ToInt24(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 3)
				throw new ArgumentException("startIndex");
			return InternalToInt24(BitConverter.IsLittleEndian, value, startIndex);
		}

		/// <summary>Returns a 24-bit signed integer converted from three bytes at a specified position in a byte array.</summary>
		/// <returns>A 24-bit signed integer formed by three bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 2, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static int ToInt24(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 3)
				throw new ArgumentException("startIndex");
			return InternalToInt24(littleEndian, value, startIndex);
		}

		/// <summary>Returns a 24-bit unsigned integer converted from three bytes at a specified position in a byte array.</summary>
		/// <returns>A 24-bit unsigned integer formed by three bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 2, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static int ToUInt24(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 3)
				throw new ArgumentException("startIndex");
			return InternalToUInt24(BitConverter.IsLittleEndian, value, startIndex);
		}

		/// <summary>Returns a 24-bit unsigned integer converted from three bytes at a specified position in a byte array.</summary>
		/// <returns>A 24-bit unsigned integer formed by three bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 2, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static int ToUInt24(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 3)
				throw new ArgumentException("startIndex");
			return InternalToUInt24(littleEndian, value, startIndex);
		}

		/// <summary>Reads a 3-byte signed integer from the current stream and advances the current position of
		///   the stream by three bytes.</summary>
		/// <param name="reader">Binary reader.</param>
		/// <returns>A 3-byte signed integer read from the current stream.</returns>
		/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		public static int ReadInt24(this BinaryReader reader)
		{
			var bin = reader as BinReader;
			if (bin != null)
				return bin.ReadInt24();
			else
				return BinConverter.InternalToInt24(BitConverter.IsLittleEndian, reader.ReadBytes(3));
		}

		/// <summary>Reads a 3-byte unsigned integer from the current stream and advances the current position of
		///   the stream by three bytes.</summary>
		/// <param name="reader">Binary reader.</param>
		/// <returns>A 3-byte unsigned integer read from the current stream.</returns>
		/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		public static int ReadUInt24(this BinaryReader reader)
		{
			var bin = reader as BinReader;
			if (bin != null)
				return bin.ReadUInt24();
			else
				return BinConverter.InternalToUInt24(BitConverter.IsLittleEndian, reader.ReadBytes(3));
		}
		#endregion

		#region int 32-bit big endian
		internal static byte[] InternalFromInt32(int value, bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			if (littleEndian)
			{
				buffer[startIndex] = (byte)value;
				buffer[startIndex + 1] = (byte)(value >> 8);
				buffer[startIndex + 2] = (byte)(value >> 16);
				buffer[startIndex + 3] = (byte)(value >> 24);
			}
			else
			{
				buffer[startIndex + 3] = (byte)value;
				buffer[startIndex + 2] = (byte)(value >> 8);
				buffer[startIndex + 1] = (byte)(value >> 16);
				buffer[startIndex] = (byte)(value >> 24);
			}
			return buffer;
		}

		internal static int InternalToInt32(bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			if (littleEndian)
			{
				return
					buffer[startIndex] |
					buffer[startIndex + 1] << 8 |
					buffer[startIndex + 2] << 16 |
					buffer[startIndex + 3] << 24;
			}
			else
			{
				return
					buffer[startIndex + 3] |
					buffer[startIndex + 2] << 8 |
					buffer[startIndex + 1] << 16 |
					buffer[startIndex] << 24;
			}
		}

		/// <summary>Returns the specified 32-bit signed integer value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetBytes(this int value)
		{
			return InternalFromInt32(value, BitConverter.IsLittleEndian, new byte[4]);
		}

		/// <summary>Returns the specified 32-bit signed integer value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetBytes(this int value, bool littleEndian)
		{
			return InternalFromInt32(value, littleEndian, new byte[4]);
		}

		/// <summary>Returns the specified 32-bit unsigned integer value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetBytes(this uint value)
		{
			return InternalFromInt32((int)value, BitConverter.IsLittleEndian, new byte[4]);
		}

		/// <summary>Returns the specified 32-bit unsigned integer value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetBytes(this uint value, bool littleEndian)
		{
			return InternalFromInt32((int)value, littleEndian, new byte[4]);
		}

		/// <summary>Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A 32-bit signed integer formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static int ToInt32(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			return InternalToInt32(BitConverter.IsLittleEndian, value, startIndex);
		}

		/// <summary>Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A 32-bit signed integer formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static int ToInt32(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			return InternalToInt32(littleEndian, value, startIndex);
		}

		/// <summary>Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A 32-bit unsigned integer formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static uint ToUInt32(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			return (uint)InternalToInt32(BitConverter.IsLittleEndian, value, startIndex);
		}

		/// <summary>Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A 32-bit unsigned integer formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static uint ToUInt32(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			return (uint)InternalToInt32(littleEndian, value, startIndex);
		}
		#endregion

		#region word 16-bit
		internal static byte[] InternalFromInt16(short value, bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			if (littleEndian)
			{
				buffer[startIndex] = (byte)value;
				buffer[startIndex + 1] = (byte)(value >> 8);
			}
			else
			{
				buffer[startIndex + 1] = (byte)value;
				buffer[startIndex] = (byte)(value >> 8);
			}
			return buffer;
		}

		internal static short InternalToInt16(bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			if (littleEndian)
			{
				return (short)(buffer[startIndex] | buffer[startIndex + 1] << 8);
			}
			else
			{
				return (short)(buffer[startIndex + 1] | buffer[startIndex] << 8);
			}
		}

		/// <summary>Returns the specified 16-bit signed integer value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 2.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetBytes(this short value)
		{
			return InternalFromInt16(value, BitConverter.IsLittleEndian, new byte[2]);
		}

		/// <summary>Returns the specified 16-bit signed integer value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 2.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetBytes(this short value, bool littleEndian)
		{
			return InternalFromInt16(value, littleEndian, new byte[2]);
		}

		/// <summary>Returns the specified 16-bit unsigned integer value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 2.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetBytes(this ushort value)
		{
			return InternalFromInt16((short)value, BitConverter.IsLittleEndian, new byte[2]);
		}

		/// <summary>Returns the specified 16-bit unsigned integer value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 2.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetBytes(this ushort value, bool littleEndian)
		{
			return InternalFromInt16((short)value, littleEndian, new byte[2]);
		}

		/// <summary>Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.</summary>
		/// <returns>A 16-bit signed integer formed by two bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> equals the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static short ToInt16(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 2)
				throw new ArgumentException("startIndex");
			return InternalToInt16(BitConverter.IsLittleEndian, value, startIndex);
		}

		/// <summary>Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.</summary>
		/// <returns>A 16-bit signed integer formed by two bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> equals the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static short ToInt16(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 2)
				throw new ArgumentException("startIndex");
			return InternalToInt16(littleEndian, value, startIndex);
		}

		/// <summary>Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.</summary>
		/// <returns>A 16-bit unsigned integer formed by two bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> equals the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static ushort ToUInt16(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 2)
				throw new ArgumentException("startIndex");
			return (ushort)InternalToInt16(BitConverter.IsLittleEndian, value, startIndex);
		}

		/// <summary>Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.</summary>
		/// <returns>A 16-bit unsigned integer formed by two bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> equals the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static ushort ToUInt16(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 2)
				throw new ArgumentException("startIndex");
			return (ushort)InternalToInt16(littleEndian, value, startIndex);
		}
		#endregion

		#region float 32-bit
		internal unsafe static byte[] InternalFromSingle(float value, bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			return InternalFromInt32(*(int*)(&value), littleEndian, buffer, startIndex);
		}

		internal unsafe static float InternalToSingle(bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			int num = InternalToInt32(littleEndian, buffer, startIndex);
			return *(float*)(&num);
		}

		/// <summary>Returns the specified single-precision floating-point value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetBytes(this float value)
		{
			return InternalFromSingle(value, BitConverter.IsLittleEndian, new byte[4]);
		}

		/// <summary>Returns the specified single-precision floating-point value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetBytes(this float value, bool littleEndian)
		{
			return InternalFromSingle(value, littleEndian, new byte[4]);
		}

		/// <summary>Returns a single-precision floating-point number converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A single-precision floating-point number formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static float ToSingle(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			return InternalToSingle(BitConverter.IsLittleEndian, value, startIndex);
		}

		/// <summary>Returns a single-precision floating-point number converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A single-precision floating-point number formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static float ToSingle(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			return InternalToSingle(littleEndian, value, startIndex);
		}
		#endregion

		#region double 64-bit
		internal unsafe static byte[] InternalFromDouble(double value, bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			return InternalFromInt64(*(long*)(&value), littleEndian, buffer, startIndex);
		}

		internal unsafe static double InternalToDouble(bool littleEndian, byte[] buffer, int startIndex = 0)
		{
			ulong num = InternalToUInt64(littleEndian, buffer, startIndex);
			return *(double*)(&num);
		}

		/// <summary>Returns the specified double-precision floating-point value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 8.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetBytes(this double value)
		{
			return InternalFromDouble(value, BitConverter.IsLittleEndian, new byte[8]);
		}

		/// <summary>Returns the specified double-precision floating-point value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 8.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetBytes(this double value, bool littleEndian)
		{
			return InternalFromDouble(value, littleEndian, new byte[8]);
		}

		/// <summary>Returns a double-precision floating-point number converted from eight bytes at a specified position in a byte array.</summary>
		/// <returns>A double-precision floating-point number formed by eight bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 7, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static double ToDouble(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 8)
				throw new ArgumentException("startIndex");
			return InternalToDouble(BitConverter.IsLittleEndian, value, startIndex);
		}

		/// <summary>Returns a double-precision floating-point number converted from eight bytes at a specified position in a byte array.</summary>
		/// <returns>A double-precision floating-point number formed by eight bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 7, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static double ToDouble(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 8)
				throw new ArgumentException("startIndex");
			return InternalToDouble(littleEndian, value, startIndex);
		}
		#endregion

		#region UNIX date
		/// <summary>Represents the largest possible value of UNIX date as 32-bit signed integer.
		///   This value on January 19, 2038 at 03:14:07. This field is read-only.</summary>
		public static readonly DateTime MaxUNIXDateI32 = new DateTime(64283080447L * TicksPerSecond); //UNIXDateToTicks(int.MaxValue)
		/// <summary>Represents the smallest possible value of UNIX date as 32-bit signed integer.
		///   This value on December 13, 1901 at 20:45:52. This field is read-only.</summary>
		public static readonly DateTime MinUNIXDateI32 = new DateTime(59988113152L * TicksPerSecond); //UNIXDateToTicks(int.MinValue)
		/// <summary>Represents the largest possible value of UNIX date as 32-bit unsigned integer.
		///   This value on February 7, 2106 at 06:28:15. This field is read-only.</summary>
		public static readonly DateTime MaxUNIXDateU32 = new DateTime(66430564095L * TicksPerSecond); //UNIXDateToTicks(uint.MaxValue)
		/// <summary>Represents the smallest possible value of UNIX date as 32-bit unsigned integer.
		///   This value on January 1, 1970 at midnight. This field is read-only.</summary>
		public static readonly DateTime MinUNIXDateU32 = new DateTime(62135596800L * TicksPerSecond); //UNIXDateToTicks(0)

		internal static long UNIXDateToTicks(long value)
		{
			if (value >= MaxSeconds - (DaysTo1970 * SecondsPerDay) || value <= -(DaysTo1970 * SecondsPerDay))
				throw new OverflowException("Invalid UNIX date value");
			long ticks = value * TicksPerSecond;
			return ticks + DaysTo1970 * TicksPerDay;
		}

		internal static long TicksToUNIXDate(long value)
		{
			long ticks = value - DaysTo1970 * TicksPerDay;
			return ticks / TicksPerSecond;
		}

		/// <summary>Returns the specified UNIX date 32-bit signed value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		/// <exception cref="T:System.OverflowException">
		///   <paramref name="value" /> is after January 18, 2038 or before December 14, 1901.</exception>
		public static byte[] GetUNIXDateI32Bytes(this DateTime value)
		{
			int sec = checked((int)TicksToUNIXDate(value.Ticks));
			return InternalFromInt32(sec, BitConverter.IsLittleEndian, new byte[4]);
		}

		/// <summary>Returns the specified UNIX date 32-bit signed value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.OverflowException">
		///   <paramref name="value" /> is after January 18, 2038 or before December 14, 1901.</exception>
		public static byte[] GetUNIXDateI32Bytes(this DateTime value, bool littleEndian)
		{
			int sec = checked((int)TicksToUNIXDate(value.Ticks));
			return InternalFromInt32(sec, littleEndian, new byte[4]);
		}

		/// <summary>Returns the specified UNIX date 32-bit unsigned value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		/// <exception cref="T:System.OverflowException">
		///   <paramref name="value" /> is after February 6, 2106 or before January 1, 1970.</exception>
		public static byte[] GetUNIXDateU32Bytes(this DateTime value)
		{
			uint sec = checked((uint)TicksToUNIXDate(value.Ticks));
			return InternalFromInt32((int)sec, BitConverter.IsLittleEndian, new byte[4]);
		}

		/// <summary>Returns the specified UNIX date 32-bit unsigned value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.OverflowException">
		///   <paramref name="value" /> is after February 6, 2106 or before January 1, 1970.</exception>
		public static byte[] GetUNIXDateU32Bytes(this DateTime value, bool littleEndian)
		{
			uint sec = checked((uint)TicksToUNIXDate(value.Ticks));
			return InternalFromInt32((int)sec, littleEndian, new byte[4]);
		}

		/// <summary>Returns the specified UNIX date 64-bit value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 8.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetUNIXDate64Bytes(this DateTime value)
		{
			long sec = TicksToUNIXDate(value.Ticks);
			return InternalFromInt64(sec, BitConverter.IsLittleEndian, new byte[8]);
		}

		/// <summary>Returns the specified UNIX date 64-bit value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 8.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetUNIXDate64Bytes(this DateTime value, bool littleEndian)
		{
			long sec = TicksToUNIXDate(value.Ticks);
			return InternalFromInt64(sec, littleEndian, new byte[8]);
		}

		/// <summary>Writes a UNIX date as 32-bit signed integer to the current stream and advances the stream position by four bytes.</summary>
		/// <param name="writer">Binary writer.</param>
		/// <param name="value">The UNIX date to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.OverflowException">
		///   <paramref name="value" /> is after January 18, 2038 or before December 14, 1901.</exception>
		public static void WriteUNIXDateI32(this BinaryWriter writer, DateTime value)
		{
			int sec = checked((int)TicksToUNIXDate(value.Ticks));
			writer.Write(sec);
		}

		/// <summary>Writes a UNIX date as 32-bit unsigned integer to the current stream and advances the stream position by four bytes.</summary>
		/// <param name="writer">Binary writer.</param>
		/// <param name="value">The UNIX date to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.OverflowException">
		///   <paramref name="value" /> is after February 6, 2106 or before January 1, 1970.</exception>
		public static void WriteUNIXDateU32(this BinaryWriter writer, DateTime value)
		{
			uint sec = checked((uint)TicksToUNIXDate(value.Ticks));
			writer.Write(sec);
		}

		/// <summary>Writes a UNIX date as 64-bit integer to the current stream and advances the stream position by eight bytes.</summary>
		/// <param name="writer">Binary writer.</param>
		/// <param name="value">The UNIX date to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public static void WriteUNIXDate64(this BinaryWriter writer, DateTime value)
		{
			long sec = TicksToUNIXDate(value.Ticks);
			writer.Write(sec);
		}

		/// <summary>Returns a UNIX date as 32-bit signed integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A UNIX date formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static DateTime ToUNIXDateI32(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			int sec = InternalToInt32(BitConverter.IsLittleEndian, value, startIndex);
			return new DateTime(UNIXDateToTicks(sec));
		}

		/// <summary>Returns a UNIX date as 32-bit signed integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A UNIX date formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static DateTime ToUNIXDateI32(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			int sec = InternalToInt32(littleEndian, value, startIndex);
			return new DateTime(UNIXDateToTicks(sec));
		}

		/// <summary>Returns a UNIX date as 32-bit unsigned integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A UNIX date formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static DateTime ToUNIXDateU32(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			uint sec = (uint)InternalToInt32(BitConverter.IsLittleEndian, value, startIndex);
			return new DateTime(UNIXDateToTicks(sec));
		}

		/// <summary>Returns a UNIX date as 32-bit unsigned integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A UNIX date formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static DateTime ToUNIXDateU32(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			uint sec = (uint)InternalToInt32(littleEndian, value, startIndex);
			return new DateTime(UNIXDateToTicks(sec));
		}

		/// <summary>Returns a UNIX date as 64-bit integer converted from eight bytes at a specified position in a byte array.</summary>
		/// <returns>A UNIX date formed by eight bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 7, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		/// <exception cref="T:System.OverflowException">
		///   A formed date is after December 31, 9999 or before January 1, 0001.</exception>
		public static DateTime ToUNIXDate64(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 8)
				throw new ArgumentException("startIndex");
			long sec = (long)InternalToUInt64(BitConverter.IsLittleEndian, value, startIndex);
			return new DateTime(UNIXDateToTicks(sec));
		}

		/// <summary>Returns a UNIX date as 64-bit integer converted from eight bytes at a specified position in a byte array.</summary>
		/// <returns>A UNIX date formed by eight bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 7, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		/// <exception cref="T:System.OverflowException">
		///   A formed date is after December 31, 9999 or before January 1, 0001.</exception>
		public static DateTime ToUNIXDate64(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 8)
				throw new ArgumentException("startIndex");
			long sec = (long)InternalToUInt64(littleEndian, value, startIndex);
			return new DateTime(UNIXDateToTicks(sec));
		}

		/// <summary>Reads a UNIX date as 32-bit signed integer from the current stream and advances the current position of
		///   the stream by four bytes.</summary>
		/// <param name="reader">Binary reader.</param>
		/// <returns>A UNIX date read from the current stream.</returns>
		/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		public static DateTime ReadUNIXDateI32(this BinaryReader reader)
		{
			int sec = reader.ReadInt32();
			return new DateTime(UNIXDateToTicks(sec));
		}

		/// <summary>Reads a UNIX date as 32-bit unsigned integer from the current stream and advances the current position of
		///   the stream by four bytes.</summary>
		/// <param name="reader">Binary reader.</param>
		/// <returns>A UNIX date read from the current stream.</returns>
		/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		public static DateTime ReadUNIXDateU32(this BinaryReader reader)
		{
			uint sec = reader.ReadUInt32();
			return new DateTime(UNIXDateToTicks(sec));
		}

		/// <summary>Reads a UNIX date as 64-bit integer from the current stream and advances the current position of
		///   the stream by eight bytes.</summary>
		/// <param name="reader">Binary reader.</param>
		/// <returns>A UNIX date read from the current stream.</returns>
		/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.OverflowException">
		///   A formed date is after December 31, 9999 or before January 1, 0001.</exception>
		public static DateTime ReadUNIXDate64(this BinaryReader reader)
		{
			long sec = reader.ReadInt64();
			return new DateTime(UNIXDateToTicks(sec));
		}
		#endregion

		#region Macintosh date
		/// <summary>Represents the largest possible value of Macintosh date as 32-bit integer.
		///   This value on February 6, 2040 at 06:28:15. This field is read-only.</summary>
		public static readonly DateTime MaxMacDate32 = new DateTime(64347719295L * TicksPerSecond); //MacDateToTicks(uint.MaxValue)
		/// <summary>Represents the smallest possible value of Macintosh date as 32-bit integer.
		///   This value on January 1, 1904 at midnight. This field is read-only.</summary>
		public static readonly DateTime MinMacDate32 = new DateTime(60052752000L * TicksPerSecond); //MacDateToTicks(0)

		internal static long MacDateToTicks(long value)
		{
			if (value >= MaxSeconds - (DaysTo1904 * SecondsPerDay) || value <= -(DaysTo1904 * SecondsPerDay))
				throw new OverflowException("Invalid macintosh date value");
			long ticks = value * TicksPerSecond;
			return ticks + DaysTo1904 * TicksPerDay;
		}

		internal static long TicksToMacDate(long value)
		{
			long ticks = value - DaysTo1904 * TicksPerDay;
			return ticks / TicksPerSecond;
		}

		/// <summary>Returns the specified Macintosh date 32-bit value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		/// <exception cref="T:System.OverflowException">
		///   <paramref name="value" /> is after February 5, 2040 or before January 1, 1904.</exception>
		public static byte[] GetMacDate32Bytes(this DateTime value)
		{
			uint sec = checked((uint)TicksToMacDate(value.Ticks));
			return InternalFromInt32((int)sec, BitConverter.IsLittleEndian, new byte[4]);
		}

		/// <summary>Returns the specified Macintosh date 32-bit value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 4.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.OverflowException">
		///   <paramref name="value" /> is after February 5, 2040 or before January 1, 1904.</exception>
		public static byte[] GetMacDate32Bytes(this DateTime value, bool littleEndian)
		{
			uint sec = checked((uint)TicksToMacDate(value.Ticks));
			return InternalFromInt32((int)sec, littleEndian, new byte[4]);
		}

		/// <summary>Returns the specified Macintosh date 64-bit value as an array of bytes.</summary>
		/// <returns>An array of bytes with length 8.</returns>
		/// <param name="value">The number to convert. </param>
		public static byte[] GetMacDate64Bytes(this DateTime value)
		{
			long sec = TicksToMacDate(value.Ticks);
			return InternalFromInt64(sec, BitConverter.IsLittleEndian, new byte[8]);
		}

		/// <summary>Returns the specified Macintosh date 64-bit value as an array of bytes in specified byte order.</summary>
		/// <returns>An array of bytes with length 8.</returns>
		/// <param name="value">The number to convert. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		public static byte[] GetMacDate64Bytes(this DateTime value, bool littleEndian)
		{
			long sec = TicksToMacDate(value.Ticks);
			return InternalFromInt64(sec, littleEndian, new byte[8]);
		}

		/// <summary>Writes a Macintosh date as 32-bit integer to the current stream and advances the stream position by four bytes.</summary>
		/// <param name="writer">Binary writer.</param>
		/// <param name="value">The Macintosh date to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.OverflowException">
		///   <paramref name="value" /> is after February 5, 2040 or before January 1, 1904.</exception>
		public static void WriteMacDate32(this BinaryWriter writer, DateTime value)
		{
			uint sec = checked((uint)TicksToMacDate(value.Ticks));
			writer.Write(sec);
		}

		/// <summary>Writes a Macintosh date as 64-bit integer to the current stream and advances the stream position by eight bytes.</summary>
		/// <param name="writer">Binary writer.</param>
		/// <param name="value">The Macintosh date to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public static void WriteMacDate64(this BinaryWriter writer, DateTime value)
		{
			long sec = TicksToMacDate(value.Ticks);
			writer.Write(sec);
		}

		/// <summary>Returns a Macintosh date as 32-bit integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A Macintosh date formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static DateTime ToMacDate32(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			uint sec = (uint)InternalToInt32(BitConverter.IsLittleEndian, value, startIndex);
			return new DateTime(MacDateToTicks(sec));
		}

		/// <summary>Returns a Macintosh date as 32-bit integer converted from four bytes at a specified position in a byte array.</summary>
		/// <returns>A Macintosh date formed by four bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 3, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		public static DateTime ToMacDate32(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 4)
				throw new ArgumentException("startIndex");
			uint sec = (uint)InternalToInt32(littleEndian, value, startIndex);
			return new DateTime(MacDateToTicks(sec));
		}

		/// <summary>Returns a Macintosh date as 64-bit integer converted from eight bytes at a specified position in a byte array.</summary>
		/// <returns>A Macintosh date formed by eight bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 7, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		/// <exception cref="T:System.OverflowException">
		///   A formed date is after December 31, 9999 or before January 1, 0001.</exception>
		public static DateTime ToMacDate64(this byte[] value, int startIndex)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 8)
				throw new ArgumentException("startIndex");
			long sec = (long)InternalToUInt64(BitConverter.IsLittleEndian, value, startIndex);
			return new DateTime(MacDateToTicks(sec));
		}

		/// <summary>Returns a Macintosh date as 64-bit integer converted from eight bytes at a specified position in a byte array.</summary>
		/// <returns>A Macintosh date formed by eight bytes beginning at <paramref name="startIndex" />.</returns>
		/// <param name="value">An array of bytes. </param>
		/// <param name="startIndex">The starting position within <paramref name="value" />. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">
		///   <paramref name="startIndex" /> is greater than or equal to the length of <paramref name="value" /> minus 7, and is less than or equal to the length of <paramref name="value" /> minus 1.</exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="value" /> is null. </exception>
		/// <exception cref="T:System.ArgumentOutOfRangeException">
		///   <paramref name="startIndex" /> is less than zero or greater than the length of <paramref name="value" /> minus 1. </exception>
		/// <exception cref="T:System.OverflowException">
		///   A formed date is after December 31, 9999 or before January 1, 0001.</exception>
		public static DateTime ToMacDate64(this byte[] value, int startIndex, bool littleEndian)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex >= value.Length)
				throw new ArgumentOutOfRangeException("startIndex");
			if (startIndex > value.Length - 8)
				throw new ArgumentException("startIndex");
			long sec = (long)InternalToUInt64(littleEndian, value, startIndex);
			return new DateTime(MacDateToTicks(sec));
		}

		/// <summary>Reads a Macintosh date as 32-bit integer from the current stream and advances the current position of
		///   the stream by four bytes.</summary>
		/// <param name="reader">Binary reader.</param>
		/// <returns>A Macintosh date read from the current stream.</returns>
		/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		public static DateTime ReadMacDate32(this BinaryReader reader)
		{
			uint sec = reader.ReadUInt32();
			return new DateTime(MacDateToTicks(sec));
		}

		/// <summary>Reads a Macintosh date as 64-bit integer from the current stream and advances the current position of
		///   the stream by eight bytes.</summary>
		/// <param name="reader">Binary reader.</param>
		/// <returns>A Macintosh date read from the current stream.</returns>
		/// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.OverflowException">
		///   A formed date is after December 31, 9999 or before January 1, 0001.</exception>
		public static DateTime ReadMacDate64(this BinaryReader reader)
		{
			long sec = reader.ReadInt64();
			return new DateTime(MacDateToTicks(sec));
		}
		#endregion
	}
}